﻿using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading;

namespace DimensionsHelper.SourceGeneration.Constants
{

    [Generator]
    public class ConstantsSourceGenerator : IIncrementalGenerator
    {

        private const string ConstantClassAttributeShortName = "ConstantsClass";
        private const string ConstantClassAttributeLongName = "ConstantsClassAttribute";
        private const string ConstantClassAttributeFullName = "DimensionsHelper.Serialization.Constants.ConstantsClassAttribute";
        private const string ConstantTargetMatcherAttributeFullName = "DimensionsHelper.Serialization.Constants.ConstantTargetMatcherAttribute";
        private const string ConstantValueAttributeFullName = "DimensionsHelper.Serialization.Constants.ConstantValueAttribute";

        private Action<Diagnostic> _diagnosticReporter;
        private static readonly Dictionary<string, ObjectInfo> _constantObjectCache = new Dictionary<string, ObjectInfo>();

        public void Initialize(IncrementalGeneratorInitializationContext context)
        {
            var symbols = context
                .SyntaxProvider
                .ForAttributeWithMetadataName(
                    ConstantClassAttributeFullName,
                    PredicateNode,
                    (c, t) => c.TargetSymbol as INamedTypeSymbol)
                .Where(e => e != null);

            context.RegisterSourceOutput(symbols, EmitSource);
        }


        private static bool PredicateNode(SyntaxNode node, CancellationToken token)
        {
            return node is ClassDeclarationSyntax classDeclaration
                && !classDeclaration.Modifiers.Any(SyntaxKind.AbstractKeyword)
                && (classDeclaration.Modifiers.Any(SyntaxKind.PublicKeyword) || classDeclaration.Modifiers.Any(SyntaxKind.InternalKeyword))
                && classDeclaration.AttributeLists.Count > 0
                && classDeclaration.AttributeLists.Any(list => list.Attributes.Any(syntax =>
                {
                    var text = syntax.Name.GetText().ToString();
                    return text == ConstantClassAttributeShortName
                        || text == ConstantClassAttributeLongName;
                }));
        }


        private void ReportDiagnostic(DiagnosticDescriptor descriptor, Location location, params object[] messageArgs)
        {
            _diagnosticReporter?.Invoke(Diagnostic.Create(descriptor, location, messageArgs));
        }


        private static ObjectInfo TryInitConstantObject(ITypeSymbol typeSymbol)
        {
            if (!(typeSymbol is INamedTypeSymbol namedTypeSymbol))
            {
                return null;
            }

            var fullName = namedTypeSymbol.ToDisplayString();
            if (_constantObjectCache.TryGetValue(fullName, out var info))
            {
                return info;
            }

            bool hasParameterlessConstructor = false;

            // 优先检查构造函数
            foreach (var ctor in namedTypeSymbol.Constructors)
            {
                if (!ctor.Parameters.IsEmpty)
                {
                    var members = new List<ObjectMemberInfo>();
                    bool succeed = true;

                    foreach (var p in ctor.Parameters)
                    {
                        var memberInfo = ProcessObjectMemberInfo(p, p.Type);

                        if (memberInfo == null)
                        {
                            succeed = false;
                            break;
                        }

                        members.Add(memberInfo);
                    }

                    if (succeed && members.Count > 0)
                    {
                        info = new ObjectInfo
                        {
                            TypeFullName = namedTypeSymbol.GetSymbolFullName(),
                            IsConstructor = true,
                            Members = members,
                        };
                    }

                    if (info != null)
                    {
                        return info;
                    }
                }
                else
                {
                    if (ctor.DeclaredAccessibility != Accessibility.Private)
                    {
                        hasParameterlessConstructor = true;
                    }
                }
            }

            // 需要不带有参数的public或internal的构造函数
            if (!hasParameterlessConstructor)
            {
                return null;
            }

            var memberList = new List<ObjectMemberInfo>();

            foreach (var member in namedTypeSymbol.GetMembers())
            {
                // 只考虑字段或属性
                if ((member.Kind != SymbolKind.Property && member.Kind != SymbolKind.Field) ||
                    member.DeclaredAccessibility == Accessibility.Private)
                {
                    continue;
                }

                var type = member is IPropertySymbol property ? property.Type 
                                                              : ((IFieldSymbol)member).Type;
                var classMemberInfo = ProcessObjectMemberInfo(member, type);

                if (classMemberInfo != null)
                {
                    memberList.Add(classMemberInfo);
                }
                else
                {
                    // 如果成员带有 required 修饰符，添加一个空白的成员对象
                    if (member is IPropertySymbol propertySymbol && propertySymbol.IsRequired)
                    {
                        memberList.Add(new ObjectMemberInfo
                        {
                            Name = propertySymbol.Name,
                            IsConstantValue = false,
                            IsEnum = false,
                            IsValueType = propertySymbol.Type.IsValueType,
                            IsRequiredDefault = true
                        }); 
                    }
                }
            }

            if (memberList.Count > 0)
            {
                return new ObjectInfo
                {
                    TypeFullName = namedTypeSymbol.GetSymbolFullName(),
                    IsConstructor = false,
                    Members = memberList
                };
            }

            return null;
        }


        private static ObjectMemberInfo ProcessObjectMemberInfo(ISymbol memberSymbol, ITypeSymbol memberType)
        {
            foreach (var attr in memberSymbol.GetAttributes())
            {
                switch (attr.AttributeClass.ToDisplayString())
                {
                    case ConstantTargetMatcherAttributeFullName when attr.ConstructorArguments.Length == 1 && attr.ConstructorArguments[0].Value is string regex:
                        return new ObjectMemberInfo
                        {
                            Mather = new Regex(regex),
                            IsConstantValue = false,
                            IsEnum = memberType.IsEnum(),
                            IsValueType = memberType.IsValueType,
                            TypeFullName = memberType.GetSymbolFullName(),
                            Name = memberSymbol.Name,
                        };

                    case ConstantValueAttributeFullName:
                        return new ObjectMemberInfo
                        {
                            Mather = null,
                            IsConstantValue = true,
                            IsEnum = false,
                            IsValueType = memberType.IsValueType,
                            TypeFullName = memberType.GetSymbolFullName(),
                            Name = memberSymbol.Name,
                        };

                    default:
                        break;
                }
            }

            return null;
        }


        private List<ConstantFieldGenerationInfoList> ReadAttributes(INamedTypeSymbol classSymbol)
        {
            var list = new List<ConstantFieldGenerationInfoList>();
            foreach (var attr in classSymbol.GetAttributes())
            {
                ReadAttribute(classSymbol, list, attr);
            }
            return list;
        }


        private void ReadAttribute(INamedTypeSymbol classSymbol, List<ConstantFieldGenerationInfoList> target, AttributeData attributeData)
        {
            if (attributeData.AttributeClass.ToDisplayString() != ConstantClassAttributeFullName)
            {
                return;
            }

            Regex validateRegex = null;
            Regex nameMatcher = null;

            // 构造函数
            // ConstantsClassAttribute(Type resourcesType, Type sourceType, Type targetType, string creator)

            // 缺少参数
            if (attributeData.ConstructorArguments.Length != 3)
            {
                return;
            }


            // 如果任意参数无效，跳过
            if (!(attributeData.ConstructorArguments[0].Value is INamedTypeSymbol resourceSymbol) ||
                !(attributeData.ConstructorArguments[1].Value is ITypeSymbol sourceTypeSymbol) ||
                !(attributeData.ConstructorArguments[2].Value is ITypeSymbol targetTypeSymbol))
            {
                return;
            }

            string creatorName = null;

            foreach (var pair in attributeData.NamedArguments)
            {
                switch (pair.Key)
                {
                    case "Validator" when pair.Value.Value is string validatorValue && !string.IsNullOrEmpty(validatorValue):
                        validateRegex = new Regex(validatorValue);
                        break;

                    case "NameMatcher" when pair.Value.Value is string nameMatherVal && !string.IsNullOrEmpty(nameMatherVal):
                        nameMatcher = new Regex(nameMatherVal);
                        break;

                    case "Creator" when pair.Value.Value is string creatorMethod && !string.IsNullOrEmpty(creatorMethod):
                        creatorName = creatorMethod;
                        break;

                    default:
                        break;
                }
            }

            if (!string.IsNullOrEmpty(creatorName))
            {
                // creator -> TTarget Method(string fieldName, TSource fieldValue)
                // 如果不存在，跳过
                if (!(classSymbol.GetMembers(creatorName).FirstOrDefault() is IMethodSymbol creatorSymbol))
                {
                    ReportDiagnostic(ConstantsDiagnostics.MethodIsNotExist,
                        attributeData.GetLocation(), creatorName);
                    return;
                }

                // 检查返回类型
                if (!targetTypeSymbol.IsAssignableFrom(creatorSymbol.ReturnType))
                {
                    ReportDiagnostic(ConstantsDiagnostics.UnmatchedType,
                        attributeData.GetLocation(),
                        creatorSymbol.ReturnType.ToDisplayString(),
                        targetTypeSymbol.ToDisplayString());
                    return;
                }
            }

            var info = new ConstantFieldGenerationInfoList
            {
                Namespace = classSymbol.ContainingNamespace.ToDisplayString(),
                ClassName = classSymbol.Name,
                ResourceClassName = resourceSymbol.Name,
                IsInternal = classSymbol.DeclaredAccessibility == Accessibility.Internal,
                IsStatic = classSymbol.IsStatic,
                Creator = creatorName,
                TargetTypeFullName = targetTypeSymbol.GetSymbolFullName(),
                TargetTypeInfo = TryInitConstantObject(targetTypeSymbol),
            };

            var resourceFullName = resourceSymbol.GetSymbolFullName();

            // Fields or properties
            foreach (var member in resourceSymbol.GetMembers())
            {
                if (!member.IsStatic || 
                    (member.Kind != SymbolKind.Field && member.Kind != SymbolKind.Property) || /** 只考虑静态字段和属性 */
                    (validateRegex != null && !validateRegex.IsMatch(member.Name)))
                {
                    continue;
                }

                string targetName = member.Name;

                if (nameMatcher != null)
                {
                    var match = nameMatcher.Match(targetName);
                    if (match.Success)
                    {
                        targetName = match.Value;
                    }
                }

                string value = $"{resourceFullName}.{member.Name}";

                ITypeSymbol type;
                if (member is IFieldSymbol field)
                {
                    type = field.Type;
                    // 判断字段是否是常量赋值
                    if (field.ConstantValue != null)
                    {
                        if (field.ConstantValue is string fieldText)
                        {
                            value = $"\"{fieldText}\"";
                        }
                        else
                        {
                            value = field.ConstantValue.ToString();
                        }
                    }
                }
                else if (member is IPropertySymbol property)
                {
                    type = property.Type;
                }
                else
                {
                    continue;
                }

                if (!SymbolEqualityComparer.Default.Equals(type, sourceTypeSymbol))
                {
                    continue;
                }
                
                info.Add(
                    targetName, 
                    member.Name, 
                    value, 
                    type.GetSymbolFullName(), 
                    member.GetSymbolDocumentationCommentXml());
            }

            if (info.Count > 0)
            {
                target.Add(info);
            }
        }


        private void EmitSource(SourceProductionContext context, INamedTypeSymbol classSymbol)
        {
            _diagnosticReporter = context.ReportDiagnostic;

            var lists = ReadAttributes(classSymbol);
            foreach (var info in lists)
            {
                // 跳过空列表
                if (info.Count == 0)
                {
                    continue;
                }

                // Creator 和 TargetTypeInfo 不能同时是空
                if (string.IsNullOrEmpty(info.Creator) && info.TargetTypeInfo == null)
                {
                    continue;
                }

                context.AddSource($"{info.ClassName}.{info.ResourceClassName}.g.cs", EmitGenerationInfoList(info).ToString());
            }
        }


        private static SourceWriter EmitGenerationInfoList(ConstantFieldGenerationInfoList list)
        {
            var writer = new SourceWriter();

            // namespace
            writer.AppendLine($@"
// <auto-generated/>

#nullable enable

namespace {list.Namespace}
{{    
");
            writer.IndentLevel++;
            writer.AppendLine($@"{(list.IsInternal ? "internal" : "public")}{(list.IsStatic ? " static" : "")} partial class {list.ClassName}
{{");

            writer.IndentLevel++;

            foreach (var info in list)
            {
                writer.AppendLine();

                if (!string.IsNullOrEmpty(info.Comment))
                {
                    writer.AppendLine(info.Comment);
                }

                writer.AppendLine($@"public static {list.TargetTypeFullName} {info.TargetPropertyName} {{ get; }} =");

                writer.IndentLevel++;

                if (list.TargetTypeInfo != null)
                {
                    writer.AppendLine(list.TargetTypeInfo.ToSyntax(info.FieldName, info.FieldValue) + ";");
                }
                else
                {
                    writer.AppendLine($"{list.Creator}(\"{info.FieldName}\", {info.FieldValue});");
                }

                writer.IndentLevel--;
            }

            writer.AppendLine();
            writer.IndentLevel--;
            writer.AppendLine('}');

            writer.IndentLevel--;
            writer.AppendLine('}');

            return writer;
        }


    }

}
